home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
hardware
/
GIO_DRVR5.1
/
gbd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
13KB
|
504 lines
/*
* Copyright (C) 1993, Silicon Graphics, Inc.
* All Rights Reserved.
*/
/* Source for a mythical GIO board device; it can be compiled for
* devices that support DMA (with or without scatter gather support),
* or for PIO mode only. This version is designed for IRIX 5.1 or later.
* Dave Olson, 5/93
*/
/* NOTE: compile with cc -coff; kernels can't be in
* ELF format for now */
/* defines for compilation; would normally be passed on compilation
* line via Makefile */
#define _K32U32 1
#define _KERNEL 1
#define IP20 1 /* define cpu type */
#if IP20 || IP22
#define R4000 1
#elif IP12
#define R3000 1
#endif
/* end of 'normal' compilation definitions */
/* the following definitions choose between PIO vs DMA supporting
* boards, and if DMA is supported, whether hardware scatter/gather
* is supported. */
#define GBD_NODMA 0 /* non-zero for PIO version of driver */
#define GBD_NUM_DMA_PGS 4 /* 0 for no hardware scatter/gather
* support, else number of pages of scatter/gather
* supported per request */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cpu.h>
#include <sys/buf.h>
#include <sys/cred.h>
#include <sys/uio.h>
#include <sys/ddi.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/edt.h>
/* NOTE: this sample driver ignores the possiblity that
* the board might be busy handling some earlier request.
* any real device must deal with that possiblity, of course,
* before changing the board registers..
*/
/* these defines and structures would normally be in a seperate
* header file */
#define GBD_BOARD_ID 0x75
#define GBD_MASK 0xff /* use 0xff if using only first byte
* of ID word, use 0xffff if using
* whole ID word
*/
#define GBD_MEMSIZE 0x8000
/* command definitions */
#define GBD_GO 1
/* state definitions */
#define GBD_SLEEPING 1
#define GBD_DONE 2
/* direction of DMA definitions */
#define GBD_READ 0
#define GBD_WRITE 1
/* status defines */
#define GBD_INTR_PEND 0x80
/* "gbd" is device prefix; also in master.d/xxx file */
/* devices interface to the board */
struct gbd_device {
int command;
int count;
int direction;
off_t offset;
unsigned *sgregisters; /* if scatter/gather supported */
caddr_t startaddr; /* if no scatter/gather on board */
unsigned status; /* errors, interrupt pending, etc. */
};
/* these are used for no scatter/gather case only, and assume
* (since they aren't protected!) that the driver is completely
* single threaded. */
struct buf *gbd_curbp[2]; /* current buffer */
caddr_t gbd_curaddr[2]; /* current address to transfer */
int gbd_curcount[2];
int gbd_totcount[2];
/* pointer to on-board registers */
volatile struct gbd_device *gbd_device[2];
char *gbd_memory[2]; /* pointer to on-board memory */
static int gbd_state[2]; /* flag for transfer state
* (PIO driver) */
void gbdintr(int, struct eframe_s *);
extern int splgio1(void);
/* early device table initialization routine. The edt
* structure is defined in edt.h.
*/
void
gbdedtinit(struct edt *e)
{
int slot, val;
/* Check to see if the device is present */
if(badaddr_val(e->e_base, sizeof(int), &val) ||
(val && GBD_MASK) != GBD_BOARD_ID) {
if (showconfig)
cmn_err (CE_CONT,
"gbdedtinit: board not installed.");
return;
}
/* figure out slot from base on VECTOR line in
* system file */
if(e->e_base == (caddr_t)0xBF400000)
slot = GIO_SLOT_0;
else if(e->e_base == (caddr_t)0xBF600000)
slot = GIO_SLOT_1;
else {
cmn_err (CE_NOTE,
"ERROR from edtinit: Bad base address %x\n", e->e_base);
return;
}
#if IP12 /* for Indigo R3000, set up board as a
* realtime bus master
*/
setgioconfig(slot,0);
#endif
#if IP20 /* for Indigo R4000, set up board as a
* realtime bus master
*/
setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_MST);
#endif
#if IP22 /* for Indigo2, set up board as a pipelined,
* realtime bus master
*/
setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_MST);
#endif
/* Save the device addresses, because
* they won't be available later. */
gbd_device[slot == GIO_SLOT_0 ? 0 : 1] =
(struct gbd_device *)e->e_base;
gbd_memory[slot == GIO_SLOT_0 ? 0 : 1] =
(char *)e->e_base2;
setgiovector(GIO_INTERRUPT_1,slot,gbdintr,0);
}
/* minor number used to indicate which slot; open does nothing
* but check that board is present. */
/* ARGSUSED */
gbdopen(dev_t *devp, int flag, int otyp, cred_t *crp)
{
if(!gbd_device[geteminor(*devp)&1])
return ENXIO; /* board not present */
return 0; /* OK */
}
/* ARGSUSED */
gbdclose(dev_t dev, int flag, int otyp, cred_t *crp)
{
return 0; /* nothing to do */
}
#ifdef GBD_NODMA
/* device write routine entry point (for character devices) */
int
gbdwrite(dev_t dev, uio_t *uio)
{
int unit = geteminor(dev)&1;
int size, err=0, s;
/* while there is data to transfer */
while((size=uio->uio_resid) > 0) {
/* Transfer no more than GBD_MEMSIZE bytes
* to the device */
size = size < GBD_MEMSIZE ? size : GBD_MEMSIZE;
/* decrements count and updates uio fields, and copies data */
if(err=uiomove(gbd_memory[unit], size, UIO_WRITE, uio))
break;
/* prevent interrupts until we sleep */
s = splgio1();
/* Transfer is complete; start output */
gbd_device[unit]->count = size;
gbd_device[unit]->command = GBD_GO;
gbd_state[unit] = GBD_SLEEPING;
while (gbd_state[unit] != GBD_DONE) {
sleep(&gbd_state[unit], PRIBIO);
}
/* restore the process level after waking up */
splx(s);
}
return err;
}
/* interrupt routine for PIO only board, just wake up
* upper half of driver
*/
/* ARGSUSED1 */
void
gbdintr(int unit, struct eframe_s *ef)
{
/* read your board's registers to determine if
* there are any errors or interrupts pending.
* If no interrupts are pending, return without
* doing anything.
*/
if(!gbd_device[unit]->status & GBD_INTR_PEND)
return;
if (gbd_state[unit] == GBD_SLEEPING) {
/* Output is complete; wake up top half
* of driver, if it is waiting */
gbd_state[unit] = GBD_DONE;
wakeup(&gbd_state[unit]);
}
/* do anything else to board to tell it we are done
* with transfer and interrupt here */
}
#else /* DMA version of driver */
void gbd_strategy(struct buf *);
/* device write routine entry point (for character devices).
* Does nothing but call uiophysio to to setup, passing a pointer
* to the gbd_strategy routine, which does most of the work.
*/
int
gbdwrite(dev_t dev, uio_t *uiop)
{
return uiophysio((int (*)())gbd_strategy, 0, dev, B_WRITE, uiop);
}
#if GBD_NUM_DMA_PGS > 0
/* Actual device setup for DMA, etc., if your board has
* hardware scatter/gather DMA support.
* Called from the gbdwrite() routine via physio().
*/
void
gbd_strategy(struct buf *bp)
{
int unit = geteminor(bp->b_dev)&1;
int npages;
volatile unsigned *sgregisters;
int i, v_addr;
/* Get address of the scatter-gather registers */
*sgregisters = gbd_device[unit]->sgregisters;
/* Get the kernel virtual address of the data; note
* b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro
* indicates false; in that case, the field bp->b_pages
* is a pointer to a linked list of pfdat structure
* pointers; that saves creating a virtual mapping and
* then decoding that mapping back to physical addresses.
* BP_ISMAPPED will never be false for character devices,
* only block devices.
*/
if(!BP_ISMAPPED(bp)) {
cmn_err(CE_WARN,
"gbd driver can't handle unmapped buffers");
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
v_addr = bp->b_dmaaddr;
/* Compute number of pages received.
* The dma_len field provides the number of pages to
* map. Note that this may be larger than the actual
* number of bytes involved in the transfer. This is
* because the transfer may cross page boundaries,
* requiring an extra page to be mapped. Limit to
* number of scatter/gather registers on board.
* Note that this sample driver doesn't handle the
* case of requests > than # of registers!
*/
npages = numpages (v_addr, bp->b_dmalen);
/*
* Provide the beginning byte offset and count to the
* device.
*/
gbd_device[unit]->offset =
(unsigned int)bp->b_dmaaddr & (NBPC-1);
if(npages > GBD_NUM_DMA_PGS) {
npages = GBD_NUM_DMA_PGS;
cmn_err(CE_WARN,
"request too large, only %d pages max", npages);
if(gbd_device[unit]->offset)
gbd_device[unit]->count = NBPC -
gbd_device[unit]->offset + (npages-1)*NBPC;
else
gbd_device[unit]->count = npages*NBPC;
bp->b_resid = bp->b_count - gbd_device[unit]->count;
}
else
gbd_device[unit]->count = bp->b_count;
/* Translate the virtual address of each page to a
* physical page number and load it into the next
* scatter-gather register. The btoct(K) macro
* converts the byte value to a page value after
* rounding down the byte value to a full page.
*/
for (i = 0; i < npages; i++) {
*sgregisters++ = btoct(kvtophys(v_addr));
/*
/* Get the next virtual address to translate.
* (NBPC is a symbolic constant for the page
* size in bytes)
*/
v_addr += NBPC;
}
if ((bp->b_flags & B_READ) == 0)
gbd_device[unit]->direction = GBD_WRITE;
else
gbd_device[unit]->direction = GBD_READ;
gbd_device[unit]->command = GBD_GO; /* start DMA */
/* and return; upper layers of kernel wait for iodone(bp) */
}
/* not much to do in this interrupt routine, since we are
* assuming for this driver that we can never have to do
* multiple DMA's to handle the number of bytes requested...
*/
void
gbdintr(int unit)
{
int error;
/* read your board's registers to determine if
* there are any errors or interrupts pending.
* If no interrupts are pending, return without
* doing anything.
*/
if(!gbd_device[unit]->status & GBD_INTR_PEND)
return;
if(error)
bp->b_flags |= B_ERROR;
iodone(bp); /* we are done, tell upper layers */
/* do anything else to board to tell it we are done
* with transfer and interrupt here */
}
#else /* GBD_NUM_DMA_PGS == 0; no hardware
* scatter/gather support */
/* Actual device setup for DMA, etc., if your board
* does NOT have hardware scatter/gather DMA support.
* Called from the gbdwrite() routine via physio().
*/
void
gbd_strategy(struct buf *bp)
{
int unit = geteminor(bp->b_dev)&1;
/* any checking for initial state here. */
/* Get the kernel virtual address of the data; note
* b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro
* indicates false; in that case, the field bp->b_pages
* is a pointer to a linked list of pfdat structure
* pointers; that saves creating a virtual mapping and
* then decoding that mapping back to physical addresses.
* BP_ISMAPPED will never be false for character devices,
* only block devices.
*/
if(!BP_ISMAPPED(bp)) {
cmn_err(CE_WARN,
"gbd driver can't handle unmapped buffers");
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
gbd_curbp[unit] = bp;
/*
* Initialize the current transfer address and count.
* The first transfer should finish the rest of the
* page, but do no more than the total byte count.
*/
gbd_curaddr[unit] = bp->b_dmaaddr;
gbd_totcount[unit] = bp->b_count;
gbd_curcount[unit] = NBPC -
((unsigned int)gbd_curaddr[unit] & (NBPC-1));
if (bp->b_count < gbd_curcount[unit])
gbd_curcount[unit] = bp->b_count;
/* Tell the device starting physical address, count,
* and direction */
gbd_device[unit]->startaddr = kvtophys(gbd_curaddr[unit]);
gbd_device[unit]->count = gbd_curcount[unit];
if (bp->b_flags & B_READ) == 0)
gbd_device[unit]->direction = GBD_WRITE;
else
gbd_device[unit]->direction = GBD_READ;
gbd_device[unit]->command = GBD_GO; /* start DMA */
/* and return; upper layers of kernel wait for iodone(bp) */
}
/* more complicated interrupt routine, not necessarily because
* board has DMA, but more typical of boards that do have
* DMA, since they are typically more complicated.
* Also more typical of devices that support block i/o, as
* opposed to character i/o.
*/
void
gbdintr(int unit)
{
int error;
register struct buf *bp = gbd_curbp[unit];
/* read your board's registers to determine if
* there are any errors or interrupts pending.
* If no interrupts are pending, return without
* doing anything.
*/
if(!gbd_device[unit]->status & GBD_INTR_PEND)
return;
if(error) {
bp->b_flags |= B_ERROR;
iodone(bp); /* we are done, tell upper layers */
}
else {
/* On successful transfer of last chunk, continue
* if necessary */
gbd_curaddr[unit] += gbd_curcount[unit];
gbd_totcount[unit] -= gbd_curcount[unit];
if(gbd_totcount[unit] <= 0)
iodone(bp);
/* we are done, tell upper layers */
else {
/* else more to do, reprogram board and
* start next dma */
gbd_curcount[unit] =
(gbd_totcount[unit] < NBPC
? gbd_totcount[unit] : NBPC);
gbd_device[unit]->startaddr =
kvtophys(gbd_curaddr[unit]);
gbd_device[unit]->count = gbd_curcount[unit];
if (bp->b_flags & B_READ) == 0)
gbd_device[unit]->direction = GBD_WRITE;
else
gbd_device[unit]->direction = GBD_READ;
gbd_device[unit]->command = GBD_GO;
/* start next DMA */
}
}
/* do anything else to board to tell it we are done
* with transfer and interrupt here */
}
#endif /* GBD_NUM_DMA_PGS */
#endif /* GBD_NODMA */